package com.guangaiboen.attendance.core.dispatcher.executor;


import com.guangaiboen.attendance.core.dispatcher.constants.DispatcherTypes;
import com.guangaiboen.attendance.core.dispatcher.context.DispatcherContext;
import com.guangaiboen.attendance.core.dispatcher.domain.DispatcherRequest;
import com.guangaiboen.attendance.core.dispatcher.domain.HttpRequest;
import com.guangaiboen.attendance.core.dispatcher.exception.DispatcherException;
import com.guangaiboen.attendance.core.dispatcher.protocol.HttpProperties;
import com.guangaiboen.attendance.core.dispatcher.utils.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;

import java.io.IOException;
import java.nio.charset.Charset;


public abstract class AbstractGenericHttpDispatcherExecutor<T> implements GenericDispatcherExecutor<T> {

    private static final Logger log = LoggerFactory.getLogger(AbstractGenericHttpDispatcherExecutor.class);

    protected static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");

    @Autowired
    protected HttpProperties httpProperties;


    @Autowired
    protected CloseableHttpClient httpClient;

    @Override
    public T dispatch(DispatcherContext dispatcherContext) {
        validate(dispatcherContext);
        T t = null;
        long startTime = System.currentTimeMillis();
        try {
            HttpRequest httpRequest = (HttpRequest) dispatcherContext.getDispatcherRequest();
            String url = httpRequest.getURL();
            t = doDispatcher(url, dispatcherContext);
            long endTime = System.currentTimeMillis();
            dispatcherContext.setTimeConsuming(calculateTimeConsuming(startTime, endTime));
            printDispatcherLogger(t, null, dispatcherContext);
        } catch (Throwable e) {
            long endTime = System.currentTimeMillis();
            dispatcherContext.setTimeConsuming(calculateTimeConsuming(startTime, endTime));
            printDispatcherLogger(t, e, dispatcherContext);
            throw new DispatcherException(e);
        }
        return t;
    }

    protected T doDispatcher(String url, DispatcherContext dispatcherContext) throws IOException {
        HttpRequestBase httpRequestBase = createHttpRequest(url, dispatcherContext);
        httpRequestBase.setConfig(getRequestConfig(dispatcherContext));
        resolveRequest(httpRequestBase);
        return execute(httpRequestBase, dispatcherContext);
    }

    public RequestConfig getRequestConfig(DispatcherContext dispatcherContext) {
        RequestConfig.Builder builder = RequestConfig.custom();
        return builder.setConnectTimeout(ObjectUtils.getOrDefault(dispatcherContext.getConnectionTimeout(),httpProperties.getConnectTimeout()))
                .setConnectionRequestTimeout(ObjectUtils.getOrDefault(dispatcherContext.getConnectionRequestTimeout(),httpProperties.getConnectionRequestTimeout()))
                .setSocketTimeout(ObjectUtils.getOrDefault(dispatcherContext.getSocketTimeout(),httpProperties.getSocketTimeout())).build();
    }

    protected abstract HttpRequestBase createHttpRequest(String url, DispatcherContext dispatcherContext);

    protected void resolveRequest(HttpUriRequest httpUriRequest){}

    protected T execute(HttpUriRequest httpUriRequest, DispatcherContext dispatcherContext) throws IOException {
        HttpEntity responseEntity = null;
        try {
            CloseableHttpResponse response = this.httpClient.execute(httpUriRequest);
            // 返回响应体的内容
            responseEntity = response.getEntity();
            if (response.getStatusLine().getStatusCode() == 200) {
                String body = EntityUtils.toString(responseEntity,DEFAULT_CHARSET);
                dispatcherContext.setResponseBody(body);
                return resolveResponse(body, dispatcherContext);
            }
            throw new DispatcherException("execute failed ! http status: "+response.getStatusLine().getStatusCode());
        } catch (DispatcherException e){
            throw e;
        }catch (Exception e) {
            throw new DispatcherException(e);
        } finally {
            EntityUtils.consume(responseEntity);
        }
    }

    protected abstract T resolveResponse(String body, DispatcherContext dispatcherContext);

    private long calculateTimeConsuming(long startTime, long endTime) {
        return endTime - startTime;
    }

    private void printDispatcherLogger(T t, Throwable e, DispatcherContext dispatcherContext) {
        if(e != null){
          //异步发送cmq 将 exceptionLog信息 保存，但是没有必要，理赔使用的是Dubbo服务，只有有异常 都会通过 ProviderExceptionFilter 记录 , 顾无须单独记录
        }
        log.info(getDispatcherLoggerContent(dispatcherContext).toString());
    }

    private StringBuffer getDispatcherLoggerContent(DispatcherContext dispatcherContext) {
        HttpRequest httpRequest = (HttpRequest) dispatcherContext.getDispatcherRequest();
        StringBuffer stringBuffer = new StringBuffer();
        String enter = System.getProperty("line.separator");
        stringBuffer.append("request type: ").append(getRequestTypes()).append(enter);
        stringBuffer.append("url: ").append(httpRequest.getURL()).append(enter);
        stringBuffer.append("time consuming: ").append(dispatcherContext.getTimeConsuming() + "ms").append(enter);
        stringBuffer.append("request body: " + dispatcherContext.getRequestBody()).append(enter);
        stringBuffer.append("response body: " + dispatcherContext.getResponseBody()).append(enter);
        return stringBuffer;
    }

    private void validate(DispatcherContext dispatcherContext) {
        HttpRequest httpRequest = (HttpRequest) dispatcherContext.getDispatcherRequest();
        Assert.notNull(dispatcherContext, "dispatcherContext is null !!");
        Assert.hasLength(httpRequest.getURL(), "url must have length; it must not be null or empty !!");
        DispatcherRequest dispatcherRequest = dispatcherContext.getDispatcherRequest();
        Assert.notNull(dispatcherRequest, "genericDispatcherRequest is null !!!");
    }

    protected abstract String getRequestTypes();

    @Override
    public String getDispatcherType() {
        return DispatcherTypes.HTTP.getDispatcherCode();
    }

}
